﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using ImageTools;
using ImageTools.IO;
using ImageTools.IO.Jpeg;
using System.IO;
using WaveMSS;

namespace Conference
{
    public partial class MainPage : UserControl
    {
        private CaptureSource captureSource = new CaptureSource(); //джерело для захоплення відео та аудіо

        private VideoBrush webcamBrush = new VideoBrush(); //пензлик для локального виводу відеопотоку

        private VideoCaptureDevice webcam = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice(); //пристрій захоплення відео

        private AudioCaptureDevice mic = CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice(); //пристрій захоплення аудіо

        private DispatcherTimer myDispatcherTimer = new DispatcherTimer(); //таймер для захоплення відео

        private DispatcherTimer myDispatcherTimer2 = new DispatcherTimer(); //таймер для захоплення аудіо

        private DispatcherTimer myDispatcherTimer3 = new DispatcherTimer(); //таймер оновлення списку користувачів

        private JpegEncoder encoder = new JpegEncoder(); //Jpeg-кодер для кадрів відеопотоку

        private JpegDecoder decoder = new JpegDecoder(); //Jpeg-декодер для кадрів відеопотоку

        private ExtendedImage eimg = new ExtendedImage(); //об'єект зовнішнього класу для збереження закодованого зображення

        private ExtendedImage eimg2 = new ExtendedImage(); //об'єект зовнішнього класу для збереження закодованого зображення

        private byte[] temp; //тимчасовий допоміжний масив байтів

        private String name = ""; //ім'я локального користувача

        private String guest = ""; //ім'я віддаленого користувача

        private Boolean sendVideo = true; //логічне значення дозволення/заборони передачі відео

        private Boolean sendAudio = true; //логічне значення дозволення/заборони передачі аудио

        private ServiceReference1.VideoServiceClient service_ref = new ServiceReference1.VideoServiceClient(); //посилання на віддалений сервіс відправленні/отримання аудіо та відео

        private AudioSinkDer audioSink = new AudioSinkDer(); //службовий клас для потокової обробки аудіофрагментів, що надходять с мікрофону

        private MediaElement media = new MediaElement(); //мультимедійний елемент для відтворення аудіо

        private Boolean check = false; //флаг для перевірки наявності у користувача необхідного обладнання

        public MainPage()
        {
            InitializeComponent(); //ініціалізація графічного інтерфейсу

            #region Initializing

            webcam.DesiredFormat = new VideoFormat(PixelFormatType.Unknown, 160, 120, 96); //встановлення формату захоплення відео

            captureSource.VideoCaptureDevice = webcam; //встановлення джерела для захоплення відео

            captureSource.AudioCaptureDevice = mic; //встановлення джерела для захоплення аудио

            audioSink.CaptureSource = captureSource; //ініціалізація допоміжного класу для захоплення 

            audioSink.CaptureSource.AudioCaptureDevice.AudioFrameSize = 1000; //встановлення розміру аудіофрагменту

            foreach (AudioFormat af in audioSink.CaptureSource.AudioCaptureDevice.SupportedFormats) //встановлення необхідного аудиоформату та відображення помилки, якщо такий формат не підтримується
                if (af.Channels == 1 && af.SamplesPerSecond == 8000 && af.BitsPerSample == 8)
                {
                    audioSink.CaptureSource.AudioCaptureDevice.DesiredFormat = af;
                    check = true;
                }
                if (check == false)
                    MessageBox.Show("Sorry, your microphone is not supported by the system...");

            media.Play(); //запуск програвання аудио

            webcamBrush.SetSource(captureSource); //встановлення джерела для відображення відео

            captureSource.CaptureImageCompleted += new EventHandler<CaptureImageCompletedEventArgs>(CaptureSource_CaptureImageCompleted); //додавання асинхронного оброблювача події

            myDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 96); //встановлення частоти дії таймеру для відео

            myDispatcherTimer.Tick += new EventHandler(Grabber); //додавання асинхронного оброблювача події

            myDispatcherTimer2.Interval = new TimeSpan(0, 0, 0, 0, 2000); //встановлення частоти дії таймеру для аудио

            myDispatcherTimer2.Tick += new EventHandler(Grabber2); //додавання асинхронного оброблювача події

            myDispatcherTimer3.Interval = new TimeSpan(0, 0, 0, 0, 5000); //встановлення частоти дії таймеру для оновлення списку користувачів

            myDispatcherTimer3.Tick += new EventHandler(Grabber3); //додавання асинхронного оброблювача події

            service_ref.FromServerVideoCompleted += new EventHandler<ServiceReference1.FromServerVideoCompletedEventArgs>(srv_FromServerVideoCompleted); //додавання асинхронного оброблювача події

            service_ref.FromServerAudioCompleted += new EventHandler<ServiceReference1.FromServerAudioCompletedEventArgs>(srv_FromServerAudioCompleted); //додавання асинхронного оброблювача події

            service_ref.GetUsersCompleted += new EventHandler<ServiceReference1.GetUsersCompletedEventArgs>(srv_GetUsersCompleted); //додавання асинхронного оброблювача події
            
            #endregion
        }

        /// <summary>
        /// Подія, яка відбувається при спрацьовуванні таймеру для захоплення відеофрагменту.
        /// </summary>
        private void Grabber(object sender, EventArgs e)
        {
            if (sendVideo) //перевірка, чи увімкнена можливість передачі відео
                captureSource.CaptureImageAsync(); //ініциювання асинхронного отримання кадру з відеопристрою
        }

        /// <summary>
        /// Подія, яка відбувається при спрацьовуванні таймеру для захоплення аудіофрагменту.
        /// </summary>
        private void Grabber2(object sender, EventArgs e)
        {
            if (sendAudio) //перевірка, чи увімкнена можливість передачі аудіо
            {
                audioSink.Stop(); //емуляція зупинки запису аудио
                service_ref.ToServerAudioAsync(name, audioSink.stream.GetBuffer()); //відправка аудиофрагменту на сервер
                service_ref.FromServerAudioAsync(guest); //ініциювання асинхронного отримання аудио з серверу
                audioSink.Start(); //емуляця старту запису аудио
            }
        }

        /// <summary>
        /// Подія, яка відбувається при спрацьовуванні таймеру для захоплення аудіофрагменту.
        /// </summary>
        private void Grabber3(object sender, EventArgs e)
        {
            service_ref.GetUsersAsync(); //ініциювання асинхронного отримання списку користувачів з серверу
        }

        /// <summary>
        /// Асинхронна процедура отримання поточного кадру з відеопотоку.
        /// </summary>
        /// <param name="e">Масив байтів, що зберігає кадр.</param>
        private void CaptureSource_CaptureImageCompleted(object sender, CaptureImageCompletedEventArgs e)
        {
            eimg = ImageTools.ImageExtensions.ToImage(e.Result); //перетворення масиву байт в зображення
            encoder.Quality = 50; //встановлення ступіня компресії
            using (MemoryStream str = new MemoryStream()) //створення тимчасового потоку
            {
                encoder.Encode(eimg, str); //компресія зображення
                temp = new byte[str.Length]; 
                str.Seek(0, SeekOrigin.Begin); //перехід до початку потоку
                str.Read(temp, 0, temp.Length); //запис в потік
                service_ref.ToServerVideoAsync(name, temp); //відправлення кадру на сервер
                service_ref.FromServerVideoAsync(guest); //ініциювання асинхронного кадру з серверу
                str.Close(); //закриття тимчасового потоку
            }
        }

        /// <summary>
        /// Асинхронна процедура отримання відеофрагменту з сервера.
        /// </summary>
        /// <param name="e">Масив байтів, що зберігає відеофрагмент.</param>
        private void srv_FromServerVideoCompleted(object sender, ServiceReference1.FromServerVideoCompletedEventArgs e)
        {
            using (MemoryStream str2 = new MemoryStream(e.Result)) //створення тимчасового потоку
            {
                decoder.Decode(eimg2, str2); //декомпресія зображення
                str2.Close(); //закриття тимчасового потоку
            }
            remoteVideo.Source = eimg2.ToBitmap(); //відображення отриманного зображення на формі
        }

        /// <summary>
        /// Асинхронна процедура отримання аудіофрагменту з сервера.
        /// </summary>
        /// <param name="e">Масив байтів, що зберігає аудіофрагмент.</param>
        private void srv_FromServerAudioCompleted(object sender, ServiceReference1.FromServerAudioCompletedEventArgs e)
        {
            WaveMediaStreamSource wmss = new WaveMediaStreamSource(new MemoryStream(e.Result)); //створення допоміжного аудиопотоку
            media.SetSource(wmss); //програвання аудио
        }

        /// <summary>
        /// Асинхронна процедура отримання списку користувачів з сервера.
        /// </summary>
        /// <param name="e">Список користувачів.</param>
        private void srv_GetUsersCompleted(object sender, ServiceReference1.GetUsersCompletedEventArgs e)
        {
            if (e.Result.Length != usersList.Items.Count) //перевірка, чи з'явилися або зникли користувачі на боці серверу
            {
                usersList.Items.Clear(); //очищення списку користувачів
                foreach (String elem in e.Result) //додавання користувачів в список
                    usersList.Items.Add(elem); 
            }
        }

        /// <summary>
        /// Початок запису аудіо та відео після натискання користувачем відповідної кнопки.
        /// </summary>
        private void startButton_Click(object sender, RoutedEventArgs e)
        {
            if (CaptureDeviceConfiguration.RequestDeviceAccess() && captureSource.VideoCaptureDevice != null && captureSource.State != CaptureState.Started) //запит дозволу на захоплення відео та аудио
            {
                localVideo.Fill = webcamBrush; //відображення локального відео
                captureSource.Start(); //початок захоплення
                myDispatcherTimer.Start(); //запуск таймеря для аудио
                myDispatcherTimer2.Start(); //запуск таймера для відео
            }
        }

        /// <summary>
        /// Припинення запису аудіо та відео після натискання користувачем відповідної кнопки.
        /// </summary>
        private void stopButton_Click(object sender, RoutedEventArgs e)
        {
            captureSource.Stop(); //зупинення захоплення
            myDispatcherTimer.Stop(); //зупинення таймеру аудио
            myDispatcherTimer2.Stop(); //зупинення таймеру відео
        }

        /// <summary>
        /// Сброба пройти авторізацію на сервері після натискання користувачем відповідної кнопки.
        /// </summary>
        private void loginButton_Click(object sender, RoutedEventArgs e)
        {
            name = nameField.Text; //запам'ятовування імені локального користувача
            service_ref.LogInAsync(name); //авторизація на сервері
            service_ref.GetUsersAsync(); //ініциювання асинхронного отримання списку користувачів з серверу
            loginButton.IsEnabled = false; //деактивація кнопки логіну
            nameField.IsEnabled = false; //деактивація поля введення логіну
            logoutButton.IsEnabled = true; //активація кнопки виходу із системи
            startButton.IsEnabled = true; //активація кнопки початку зв'язку
            stopButton.IsEnabled = true; //активація кнопки закінчення зв'язку
            videoFlag.IsEnabled = true; //активація елементу дозволення передачі відео
            audioFlag.IsEnabled = true; //активація елементу дозволення передачі аудио
            myDispatcherTimer3.Start(); //запуск таймеру оновлення списку користувачів
        }

        /// <summary>
        /// Вихід из системи після натискання користувачем відповідної кнопки.
        /// </summary>
        private void logoutButton_Click(object sender, RoutedEventArgs e)
        {
            loginButton.IsEnabled = true; //активація кнопки логіну
            logoutButton.IsEnabled = false; //деактивація кнопки виходу із системи
            nameField.IsEnabled = true; //активація поля введення логіну
            startButton.IsEnabled = false; //деактивація кнопки початку зв'язку
            stopButton.IsEnabled = false; //деактивація кнопки закінчення зв'язку
            videoFlag.IsEnabled = false; //деактивація елементу дозволення передачі відео
            audioFlag.IsEnabled = false; //деактивація елементу дозволення передачі відео
            myDispatcherTimer.Stop(); //зупинення таймеру передачі відео
            myDispatcherTimer2.Stop(); //зупинення таймеру передачі аудио
            myDispatcherTimer3.Stop(); //зупинення таймеру оновлення списку користувачів
            usersList.Items.Clear(); //очищення списку користувачів
            service_ref.LogOutAsync(name); //вихід користувача з серверу
        }

        /// <summary>
        /// Збереження в локальній змінній им'я віддаленого користувача піля натискання на його ім'я в списку. 
        /// </summary>
        private void usersList_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            guest = usersList.SelectedItem.ToString();
        }

        /// <summary>
        /// Вимкнення передачі відео.
        /// </summary>
        private void videoFlag_Checked(object sender, RoutedEventArgs e)
        {
            sendVideo = false;
        }

        /// <summary>
        /// Увімкнення передачі відео.
        /// </summary>
        private void videoFlag_Unchecked(object sender, RoutedEventArgs e)
        {
            sendVideo = true;
        }

        /// <summary>
        /// Вимкнення передачі аудио.
        /// </summary>
        private void audioFlag_Checked(object sender, RoutedEventArgs e)
        {
            sendAudio = false;
        }

        /// <summary>
        /// Увімкнення передачі аудио.
        /// </summary>
        private void audioFlag_Unchecked(object sender, RoutedEventArgs e)
        {
            sendAudio = true;
        }
    }
}
